package net.omnivention.proxy;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.log4j.Logger;

public class HostToClient implements Runnable {
	private static Logger logger = Logger.getLogger(HostToClient.class);

	private HttpRequest request;

	private Socket clientSocket;

	private Socket destinationHostSocket;

	private long timeout = 12000;

	public HostToClient(HttpRequest request, Socket clientSocket, Socket hostSocket) {
		this.request = request;
		this.clientSocket = clientSocket;	
		this.destinationHostSocket = hostSocket;
	}

	public void run() {
		try {
			String method = request.getMethod();
			if (HttpRequest.CONNECT.equals(method)) {
				OutputStream clientOutputStream = clientSocket.getOutputStream();
				BufferedOutputStream clientBufferedOutputStream = new BufferedOutputStream(clientOutputStream);
				sendConnectionEstablishToClient(clientBufferedOutputStream);
				streamDataFromHostToClient(clientBufferedOutputStream);
			} else {
				InputStream hostInputStream = destinationHostSocket.getInputStream();
				BufferedInputStream hostBufferedInputStream = new BufferedInputStream(hostInputStream);

				long delta = 0;
				long time0 = System.currentTimeMillis(), time1 = 0;
				String connection = request.getHeader().getHeaderValue("connection");
				logger.info("Connection=" + connection);
				while (delta < timeout) {
					int avail = hostBufferedInputStream.available();
					if (avail > 0) {
						HttpResponse response = HttpUtility.parseResponse(hostBufferedInputStream);

						OutputStream clientOutputStream = clientSocket.getOutputStream();
						BufferedOutputStream clientBufferedOutputStream = new BufferedOutputStream(clientOutputStream);

						logger.info("\n" + response);
						clientBufferedOutputStream.write(response.getAsByteArray());
						clientBufferedOutputStream.flush();
						if ("close".equals(connection)) {
							break;
						}
						time0 = System.currentTimeMillis();
					} else {
						time1 = System.currentTimeMillis();
						delta = time1 - time0;
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
						}
					}
				}
			}
		} catch (IOException ex) {
			logger.error(ex);
			ex.printStackTrace();
		} finally {
			try {
				destinationHostSocket.close();
				logger.info("closed " + destinationHostSocket);
				clientSocket.close();
				logger.info("closed " + clientSocket);
			} catch (IOException e) {

			}

		}
	}

	private void streamDataFromHostToClient(OutputStream clientOutputStream) throws IOException {
		InputStream hostInputStream = destinationHostSocket.getInputStream();
		BufferedInputStream hostBufferedInputStream = new BufferedInputStream(hostInputStream);

		long delta = 0;
		long time0 = System.currentTimeMillis(), time1 = 0;

		while (delta < timeout) {
			int avail = hostBufferedInputStream.available();
			if (avail > 0) {
				byte[] data = new byte[avail];
				hostBufferedInputStream.read(data);
				clientOutputStream.write(data);
				clientOutputStream.flush();
			} else {
				time1 = System.currentTimeMillis();
				delta = time1 - time0;
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	/**
	 * According to spects at http://www.web-cache.com/Writings/Internet-Drafts/draft-luotonen-web-proxy-tunneling-01.txt Client
	 * will wait until proxy sends HTTP/1.1 200 Connection established\r\n
	 * 
	 * @param clientOutpuStream
	 * @throws IOException
	 */
	private void sendConnectionEstablishToClient(OutputStream clientOutpuStream) throws IOException {
		String r = "HTTP/1.1 200 Connection established\r\nProxy-agent: OmniProxy\r\n\r\n";
		clientOutpuStream.write(r.getBytes());
		clientOutpuStream.flush();
	}

}
